#include "qelib.h"
#include "sdtrace.h"
#include <pthread.h>



#define SDTRACE_LOGDOMAIN       "sdtrace"
#define sdtrace_debug(...)      qelog_debug(SDTRACE_LOGDOMAIN, __VA_ARGS__)
#define sdtrace_info(...)       qelog_info(SDTRACE_LOGDOMAIN, __VA_ARGS__)
#define sdtrace_notice(...)     qelog_notice(SDTRACE_LOGDOMAIN, __VA_ARGS__)
#define sdtrace_warning(...)    qelog_warning(SDTRACE_LOGDOMAIN, __VA_ARGS__)
#define sdtrace_critical(...)   qelog_critical(SDTRACE_LOGDOMAIN, __VA_ARGS__)
#define sdtrace_error(...)      qelog_error(SDTRACE_LOGDOMAIN, __VA_ARGS__)
#define sdtrace_fatal(...)	    qelog_fatal(SDTRACE_LOGDOMAIN, __VA_ARGS__)



#define SDTRACE_TASK_NAME       "sdtrace"
#define SDTRACE_START_MARK      "sdtrace-start"
#define SDTRACE_END_MARK        "sdtrace-end"
#define SDTRACE_VERSION_MAJOR   (1)
#define SDTRACE_VERSION_MINOR   (0)
#define SDTRACE_VERSION_PATCH   (0)



static void record_data_init(sdtrace_record_data *rd, qe_uint size, qe_uint num)
{
    /* Strat mark ='sdtrace-strat' */
    qe_sprintf(rd->start_mark, SDTRACE_START_MARK);

    rd->version = qe_version_encode(SDTRACE_VERSION_MAJOR,
        SDTRACE_VERSION_MINOR, SDTRACE_VERSION_PATCH);

    rd->size            = sizeof(sdtrace_record_data) + size * num;
    rd->num_datas       = 0;
    rd->max_datas       = num;
    rd->serialize_count = 0;
    rd->next_free       = 0;
    rd->is_full         = 0;
    rd->is_active       = 0;
    rd->data_start_mark = 0x44464F53;
    rd->data_end_mark   = 0x44464F45;

    /* End mark ='sdtrace-end' */
    qe_sprintf(rd->end_mark, SDTRACE_END_MARK);
}

sdtracer *sdtracer_new(qe_uint size, qe_uint num)
{
    sdtracer *tracer;

    tracer = qe_new(sdtracer);
    if (!tracer) {
        sdtrace_error("alloc mem for tracer failed");
        return QE_NULL;
    }
    qe_memset(tracer, 0, sizeof(sdtracer));

    tracer->ring = qe_ringq_create(size, num);
    if (!tracer->ring) {
        sdtrace_error("alloc mem for ring failed");
        qe_free(tracer);
        return QE_NULL;
    }

    record_data_init(&tracer->record, size, num);

    tracer->is_initialized = 1;
    tracer->record.is_active = 1;
    tracer->stream = QE_NULL;

    sdtrace_debug("tracer create success, size:%d num:%d", size, num);

    return tracer;
}

qe_ret sdtracer_set_stream(sdtracer *tracer, sdtrace_stream *stream)
{
    if (!tracer || !stream) {
        sdtrace_error("invalid params");
        return qe_err_param;
    }

    if (!tracer->is_initialized) {
        sdtrace_error("tracer not init");
        return qe_err_param;
    }

    if (!stream->read) {
        sdtrace_error("read function null");
        return qe_err_param;
    }

    if (!stream->write) {
        sdtrace_error("write function null");
        return qe_err_param;
    }

    tracer->stream = stream;

    return qe_ok;
}

qe_ret sdtracer_record(sdtracer *tracer, qe_const_ptr data)
{
    if (!tracer->is_initialized) {
        sdtrace_error("tracer not init");
        return qe_err_param;
    }

    if (!data) {
        sdtrace_error("data null");
        return qe_err_param;
    }

    qe_ringq_enq(tracer->ring, (void *)data, 1);

    tracer->record.num_datas = qe_ringq_wait(tracer->ring);
    tracer->record.is_full   = qe_ringq_isfull(tracer->ring);
    tracer->record.next_free = (tracer->record.next_free + 1) % tracer->record.max_datas;

    return qe_ok;
}

qe_ret sdtracer_load(sdtracer *tracer)
{
    char *start_pos;
    qe_ret ret;
    qe_int n;
    qe_uint length;
    sdtrace_stream *stream;
    sdtrace_record_data *rd;
    sdtrace_record_data record;

    if (!tracer) {
        sdtrace_error("tracer null");
        return qe_err_param;
    }

    if (!tracer->is_initialized) {
        sdtrace_error("tracer not init");
        return qe_err_param;
    }

    if (!tracer->stream) {
        sdtrace_error("tracer has no stream");
        return qe_err_param;
    }

    if (!tracer->stream->read) {
        sdtrace_error("stream no read function");
        return qe_err_param;
    }

    rd = &tracer->record;
    stream = tracer->stream;

    sdtrace_info("tracer load start, record size %d", rd->size);

    if (stream->open) {
        ret = stream->open(stream, SDTRC_STREAM_RB, rd->size);
        if (ret != qe_ok) {
            sdtrace_error("stream open error:%d", ret);
            return ret;
        }
        sdtrace_debug("stream open success");
    }

    /* read prefix */
    length = record.datas - (qe_u8 *)&record;
    sdtrace_debug("read prefix %d", length);
    n = stream->read(stream, (qe_u8 *)&record, length);
    if (n < 0) {
        sdtrace_error("read prefix error");
        ret = qe_err_read;
        goto __exit;
    }
    sdtrace_debug("prefix %d bytes readed", n);

    if (qe_strcmp(record.start_mark, SDTRACE_START_MARK) != 0) {
        sdtrace_error("start mark not match");
        ret = qe_err_match;
        goto __exit;
    }

    if (record.version != rd->version) {
        sdtrace_error("version not same %d.%d.%d", 
            SDTRACE_VERSION_MAJOR,
            SDTRACE_VERSION_MINOR,
            SDTRACE_VERSION_PATCH);
        ret = qe_err_match;
        goto __exit;
    }

    if (record.size != rd->size) {
        sdtrace_error("size not same %d", rd->size);
        ret = qe_err_match;
        goto __exit;
    }

    if (record.max_datas != rd->max_datas) {
        sdtrace_error("max not same %d", rd->max_datas);
        ret = qe_err_match;
        goto __exit;
    }

    /* read serialized datas into  */
    sdtrace_debug("read datas %d", qe_ringq_nbytes(tracer->ring));
    n = stream->read(stream,
        qe_ringq_buf_pos(tracer->ring),
        qe_ringq_nbytes(tracer->ring));
    if (n < 0) {
        sdtrace_error("read datas error");
        ret = qe_err_read;
        goto __exit;
    }
    sdtrace_debug("datas %d bytes readed", n);

    tracer->ring->count = record.num_datas;
    tracer->ring->tail  = record.next_free * tracer->ring->elem_size;
    sdtrace_debug("ring count:%d tail:%d", 
        tracer->ring->count, tracer->ring->tail);

    rd->num_datas       = record.num_datas;
    rd->next_free       = record.next_free;
    rd->serialize_count = record.serialize_count;
    rd->is_full         = record.is_full;
    rd->is_active       = record.is_active;

    sdtrace_info("tracer load finish");
    sdtrace_info("version         : %d.%d.%d",
        (record.version >> 16) & 0xFF,
        (record.version >>  8) & 0xFF,
        (record.version)       & 0xFF);
    sdtrace_info("size            : %d", rd->size);
    sdtrace_info("num datas       : %d", rd->num_datas);
    sdtrace_info("max datas       : %d", rd->max_datas);
    sdtrace_info("serialize count : %d", rd->serialize_count);
    sdtrace_info("next free       : %d", rd->next_free);
    sdtrace_info("is full         : %d", rd->is_full);
    sdtrace_info("is active       : %d", rd->is_active);

    ret = qe_ok;

__exit:
    /* close stream */
    if (stream->close) {
        stream->close(stream);
    }
    return ret;
}

qe_ret sdtracer_load_from_gbuf(sdtracer *tracer, qe_gbuf *buf)
{
    char *start_pos;
    qe_ret ret;
    qe_int n;
    qe_uint length;
    sdtrace_record_data *rd;
    sdtrace_record_data record;

    if (!tracer) {
        sdtrace_error("tracer null");
        return qe_err_param;
    }

    if (!tracer->is_initialized) {
        sdtrace_error("tracer not init");
        return qe_err_param;
    }

    if (!buf) {
        sdtrace_error("buffer null");
        return qe_err_param;
    }

    rd = &tracer->record;

    sdtrace_info("tracer load start, record size %d", rd->size);

    /* unpack prefix */
    length = record.datas - (qe_u8 *)&record;
    qe_memcpy(&record, qe_gbuf_pos(buf), length);
    sdtrace_debug("unpack prefix %d bytes", length);
    if (qe_strcmp(record.start_mark, SDTRACE_START_MARK) != 0) {
        sdtrace_error("start mark not match");
        return qe_err_match;
    }

    if (record.version != rd->version) {
        sdtrace_error("version not same %d.%d.%d", 
            SDTRACE_VERSION_MAJOR,
            SDTRACE_VERSION_MINOR,
            SDTRACE_VERSION_PATCH);
        return qe_err_match;
    }

    if (record.size != rd->size) {
        sdtrace_error("size not same %d", rd->size);
        return qe_err_match;
    }

    if (record.max_datas != rd->max_datas) {
        sdtrace_error("max not same %d", rd->max_datas);
        return qe_err_match;
    }

    /* unpack serialized datas into  */
    qe_memcpy(qe_ringq_buf_pos(tracer->ring), qe_gbuf_offs_pos(buf, length), 
        qe_ringq_nbytes(tracer->ring));
    sdtrace_debug("unpack datas %d bytes", qe_ringq_nbytes(tracer->ring));

    tracer->ring->count = record.num_datas;
    tracer->ring->tail  = record.next_free * tracer->ring->elem_size;
    sdtrace_debug("ring count:%d tail:%d", 
        tracer->ring->count, tracer->ring->tail);

    rd->num_datas       = record.num_datas;
    rd->next_free       = record.next_free;
    rd->serialize_count = record.serialize_count;
    rd->is_full         = record.is_full;
    rd->is_active       = record.is_active;

    sdtrace_info("tracer load finish");
    sdtrace_info("version         : %d.%d.%d",
        (record.version >> 16) & 0xFF,
        (record.version >>  8) & 0xFF,
        (record.version)       & 0xFF);
    sdtrace_info("size            : %d", rd->size);
    sdtrace_info("num datas       : %d", rd->num_datas);
    sdtrace_info("max datas       : %d", rd->max_datas);
    sdtrace_info("serialize count : %d", rd->serialize_count);
    sdtrace_info("next free       : %d", rd->next_free);
    sdtrace_info("is full         : %d", rd->is_full);
    sdtrace_info("is active       : %d", rd->is_active);

    return qe_ok;
}

qe_ret sdtracer_serialize(sdtracer *tracer)
{
    qe_u8 *pos;
    qe_ret ret;
    qe_int n, rn;
    qe_uint length;
    sdtrace_stream *stream;
    sdtrace_record_data *rd;

    if (!tracer) {
        sdtrace_error("tracer null");
        return qe_err_param;
    }

    if (!tracer->is_initialized) {
        sdtrace_error("tracer not init");
        return qe_err_param;
    }

    if (!tracer->stream) {
        sdtrace_error("tracer has no stream");
        return qe_err_param;
    }

    if (!tracer->stream->write) {
        sdtrace_error("stream no write function");
        return qe_err_param;
    }

    rd = &tracer->record;
    stream = tracer->stream;

    if (stream->open) {
        ret = stream->open(stream, SDTRC_STREAM_WB, rd->size);
        if (ret != qe_ok) {
            sdtrace_error("stream open error:%d", ret);
            return ret;
        }
        sdtrace_debug("stream open success");
    }

    n = 0;
    rn = 0;
    rd->serialize_count++;

    sdtrace_info("tracer serialize start");
    sdtrace_info("version         : %d.%d.%d", SDTRACE_VERSION_MAJOR, 
        SDTRACE_VERSION_MINOR, SDTRACE_VERSION_PATCH);
    sdtrace_info("size            : %d", rd->size);
    sdtrace_info("num datas       : %d", rd->num_datas);
    sdtrace_info("max datas       : %d", rd->max_datas);
    sdtrace_info("serialize count : %d", rd->serialize_count);
    sdtrace_info("next free       : %d", rd->next_free);
    sdtrace_info("is full         : %d", rd->is_full);
    sdtrace_info("is active       : %d", rd->is_active);

    /* write before datas */
    pos = (qe_u8 *)rd;
    length = (qe_u8 *)&rd->datas - pos;
    sdtrace_debug("write prefix %p %d", pos, length);
    n = stream->write(stream, pos, length);
    if (n < 0) {
        sdtrace_error("write prefix error");
        ret = qe_err_write;
        goto __exit;
    }
    sdtrace_debug("prefix %d bytes writed", n);
    rn += n;

    /* write datas */
    pos = qe_ringq_buf_pos(tracer->ring);
    length = qe_ringq_nbytes(tracer->ring);
    sdtrace_debug("write datas %p %d", pos, length);
    n = stream->write(stream, pos, length);
    if (n < 0) {
        sdtrace_error("write data error");
        ret = qe_err_write;
        goto __exit;
    }
    sdtrace_debug("datas %d bytes writed", n);
    rn += n;

    /* write after datas */
    pos = (qe_u8 *)&rd->data_end_mark;
    length = (qe_u8 *)&rd->end_mark + sizeof(rd->end_mark) - pos;
    sdtrace_debug("write postfix %p %d", pos, length);
    n = stream->write(stream, pos, length);
    if (n < 0) {
        sdtrace_error("write postfix error");
        ret = qe_err_write;
        goto __exit;
    }
    sdtrace_debug("postfix %d bytes writed", n);
    rn += n;

    sdtrace_info("tracer serialize finish, %d bytes writed", rn);

    ret = qe_ok;

__exit:
    /* close stream */
    if (stream->close) {
        stream->close(stream);
    }
    return ret;
}

qe_ret sdtracer_serialize_to_gbuf(sdtracer *tracer, qe_gbuf **pbuf)
{
    qe_ret ret;
    qe_int n;
    qe_uint length;
    qe_gbuf *buf;
    sdtrace_record_data *rd;

    if (!tracer) {
        sdtrace_error("tracer null");
        return qe_err_param;
    }

    if (!tracer->is_initialized) {
        sdtrace_error("tracer not init");
        return qe_err_param;
    }

    if (!buf) {
        sdtrace_error("buffer null");
        return qe_err_param;
    }

    rd = &tracer->record;
    buf = qe_gbuf_new(rd->size);
    if (!buf) {
        sdtrace_error("alloc mem for buffer failed");
        return qe_err_mem;
    }

    n = 0;
    rd->serialize_count++;

    sdtrace_info("tracer serialize start");
    sdtrace_info("version         : %d.%d.%d", SDTRACE_VERSION_MAJOR, 
        SDTRACE_VERSION_MINOR, SDTRACE_VERSION_PATCH);
    sdtrace_info("size            : %d", rd->size);
    sdtrace_info("num datas       : %d", rd->num_datas);
    sdtrace_info("max datas       : %d", rd->max_datas);
    sdtrace_info("serialize count : %d", rd->serialize_count);
    sdtrace_info("next free       : %d", rd->next_free);
    sdtrace_info("is full         : %d", rd->is_full);
    sdtrace_info("is active       : %d", rd->is_active);

    /* pack prefix */
    length = (qe_u8 *)&rd->data_start_mark + sizeof(rd->data_start_mark) - (qe_u8 *)rd;
    qe_gbuf_append(buf, rd, length);
    sdtrace_debug("append prefix %d bytes", length);

    /* pack datas */
    length = qe_ringq_nbytes(tracer->ring);
    qe_gbuf_append(buf, qe_ringq_buf_pos(tracer->ring), length);
    sdtrace_debug("append datas %d bytes", length);

    /* pack postfix */
    length = (qe_u8 *)&rd->end_mark + sizeof(rd->end_mark) - rd->datas;
    qe_gbuf_append(buf, &rd->end_mark, length);
    sdtrace_debug("append postfix %d", length);

    sdtrace_info("tracer serialize finish, %d bytes", qe_gbuf_data_size(buf));

    *pbuf = buf;

    return qe_ok;
}

void sdtracer_destroy(sdtracer *tracer)
{
    if (!tracer)
        return;

    if (!tracer->is_initialized) {
        sdtrace_warning("tracer not init");
        return;
    }

    if (tracer->ring)
        qe_free(tracer);

    qe_free(tracer);
}